package com.cloud.basic.reflection;

import java.lang.reflect.*;

/**
 * @program: basic
 * @description: 反射
 * @author: Cloud
 * @create: 2020/11/27 13:43:36
 */
public class reflection {

    static class Inner{}

    public static void main(String[] args) {
         Car car = new Car();

        // try {
            // Class的获取
            // Class clz = car.getClass();
            // Class clz1 = int.class;
            // Class clz2 = Class.forName("com.cloud.basic.reflection.Car");
            // Class clz3 = int[].class;
            // Class<Inner> clz4 = Inner.class;

            // Class的名字
            // 因为 Class 是一个入口，它代表引用、基本数据类型甚至是数组对象，所以获取它们的方式又有一点不同。
            // System.out.println("clz.getName() = " + clz.getName());
            // System.out.println("clz1.getName() = " + clz1.getName());
            // System.out.println("clz3.getName() = " + clz3.getName());
            // System.out.println("Void.class.getName() = " + Void.class.getName());

            // System.out.println("clz.getSimpleName() = " + clz.getSimpleName());
            // System.out.println("clz1.getSimpleName() = " + clz1.getSimpleName());
            // System.out.println("clz3.getSimpleName() = " + clz3.getSimpleName());

            // 静态内部类
            // System.out.println("clz4.getName() = " + clz4.getName());
            // System.out.println("clz4.getSimpleName() = " + clz4.getSimpleName());

            /**
             * Canonical 是官方、标准的意思，那么 getCanonicalName() 自然就是返回一个 Class 对象的官方名字，
             * 这个官方名字 canonicalName 是 Java 语言规范制定的，如果 Class 对象没有 canonicalName 的话就返回 null。
             *
             * getCanonicalName() 是 getName() 和 getSimpleName() 的结合。
             * getCanonicalName() 返回的也是全限定类名，但是对于内部类，不用 $ 开头，而用 .。
             * getCanonicalName() 对于数组类型的 Class，同 SimpleName 一样直接在后面添加 [] 。
             * getCanonicalName() 不同于 SimpleName 的地方是，不存在 canonicalName 的时候返回 null 而不是空字符串。
             * 局部类和匿名内部类不存在 canonicalName。
             * */
            // System.out.println("clz.getCanonicalName() = " + clz.getCanonicalName());
            // System.out.println("clz1.getCanonicalName() = " + clz1.getCanonicalName());
            // System.out.println("clz3.getCanonicalName() = " + clz3.getCanonicalName());
            // System.out.println("clz4.getCanonicalName() = " + clz4.getCanonicalName());

            // local 是局部类
            // class local{};

            // System.out.println("Local a name:"+local.class.getName());
            // System.out.println("Local a SimpleName:"+local.class.getSimpleName());
            // System.out.println("Local a CanonicalName:"+local.class.getCanonicalName());

            // Class 获取修饰符
            /**
             * 用来限制作用域，如 public、protected、private。
             * 用来提示子类复写，abstract。
             * 用来标记为静态类 static。
             * 注解。
             * */
            // System.out.println("TestModifier.class.getModifiers() = " + TestModifier.class.getModifiers());
            // System.out.println("Modifier.toString(TestModifier.class.getModifiers()) = " + Modifier.toString(TestModifier.class.getModifiers()));
            // 对于为什么getModifiers()得到的是int型的值，可以查看下Modifier的类，里面考虑到位运算，用int值记录所有的修饰符

            // 获取 Class 的成员


            // } catch (ClassNotFoundException e) {
            // e.printStackTrace();
            // }

        // 获取所有的属性，但不包括从父类继承下来的属性
        // public Field[] getDeclaredFields() throws SecurityException {}

        // 获取自身的所有的 public 属性，包括从父类继承下来的。
        // getFields()

        // getDeclaredField() 可以获取到该类的所有的属性，但获取不到父类
        // getField() 方法获取的是非私有属性
//        Class<Son> clz5 = Son.class;
//        Field[] fields = clz5.getFields();
//        for (Field field : fields) {
//            System.out.println("field = " + field);
//        }
//
//        Field[] declaredFields = clz5.getDeclaredFields();
//        for (Field declaredField : declaredFields) {
//            System.out.println("declaredField = " + declaredField);
//        }
//
//        try {
//            Field c = clz5.getDeclaredField("c");
//        } catch (NoSuchFieldException e) {
//            e.printStackTrace();
//        }
//
//        try {
//            Field a = clz5.getField("a");
//        } catch (NoSuchFieldException e) {
//            e.printStackTrace();
//        }

        // 获取method，跟前面的Filed一样

        // 获取构造器(Constructor)
        /**
         * public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
         *
         * public Constructor<T> getConstructor(Class<?>... parameterTypes)
         *
         * public Constructor<?>[] getDeclaredConstructors() throws SecurityException
         *
         * public Constructor<?>[] getConstructors() throws SecurityException
         * */
        // 对于构造器，唯一一点想说的是，因为，Constructor 不能从父类继承，
        // 所以就没有办法通过 getConstructor() 获取到父类的 Constructor。

        // field的操控
        // 在Class对象中，它们的属性要么是基本数据类型，要么是引用类型，所有的引用类型都是Object的后代。
        // 获取field的类型，可用如下的方法：
        /**
         * public Type getGenericType() {}
         *
         * public Class<?> getType() {}
         * */
        // 其中，getGenericType() 方法能够获取到泛型类型
//        Field[] fields1 = clz5.getFields();
//        for (Field field1 : fields1) {
//            System.out.println("field1.getName() = " + field1.getName());
//            System.out.println("field1.getType() = " + field1.getType());
//            System.out.println("field1.getGenericType() = " + field1.getGenericType());
//            System.out.println("field1.getModifiers() = " + Modifier.toString(field1.getModifiers()));
//        }

        // Class 本身不对成员进行储存，它只提供检索，所以需要用 Field、Method、Constructor 对象来承载这些成员，
        // 所以，针对成员的操作时，一般需要为成员指定类的实例引用。如果难于理解的话，可以这样理解，
        // 班级这个概念是一个类，一个班级有几十名学生，现在有A、B、C 3 个班级，将所有班级的学生抽出来集合到一个场地来考试，
        // 但是学生在试卷上写上自己名字的时候，还要指定自己的班级，这里涉及到的 Object 其实就是类似的作用，
        // 表示这个成员是具体属于哪个 Object。这个是为了精确定位。

//        Class clz6 = car.getClass();
//        Method[] methods = clz6.getMethods();
//        for (Method method : methods) {
//            System.out.println(method.getName());
//            Parameter[] parameters = method.getParameters();
//            for (Parameter parameter : parameters) {
//                System.out.println("parameter.getName() = " + parameter.getType().getName());
//            }
//        }
        // 通过反射获取到Class对象，
        Class<Arrayli> clz7 = Arrayli.class;
        try {
            // 获取该Class对象的实例
            Arrayli arrayli = clz7.newInstance();

            // 获取数组字段
            Field field = clz7.getDeclaredField("materials");
            // 设置可以访问私有private的字段属性
            field.setAccessible(true);
            // new一个数组实例。接下来是赋值
            Object o = Array.newInstance(String.class, 3);
            Array.set(o,0,"张三");
            Array.set(o,1,"李四");
            Array.set(o,2,"王五");
            // 赋值给数组字段
            field.set(arrayli,o);

            for (String material : arrayli.getMaterials()) {
                System.out.println("material = " + material);
            }

        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }

        // 获取GetCompany的Class对象
        Class<GetCompany> clz = GetCompany.class;
        try {
            // new一个获取GetCompany实例对象
            GetCompany company = new GetCompany();
            // 获取该对象的一个字段
            Field field = clz.getDeclaredField("company");
            field.setAccessible(true);
            // 设置枚举属性值
            field.set(company,Company.Amazon);
            System.out.println("company.getCompany() = " + company.getCompany());
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }
}
